/** 
 * @main Plik z definicjami klasy obiektu dyskretnego typu ARMAX.
 * @file    armax.cpp
 * @author  Grzegorz Sejnota
 *
 * @created on 23 grudzien 2008, 16:47
 * @version 0.0001
 */


#include "armax.h"
#include "wyjatki.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cmath>
#include <complex>
#include <numeric>
using namespace std;

template <typename T>
void wypiszVector(vector<T> wektor) {
    for (int i = 0; i < wektor.size(); i++) {
        cout << endl << "\t" << wektor[i];
    }
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++ POCZĄTEK [klasy ObiektARMAX] ++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//======= definicja skladnika statycznego liczbaObiektowARMAX ==================
/**
 * skladnik statyczny do przechowywania aktualnej liczby obiektow ARMAX
 */
int ObiektARMAX::liczbaObiektowARMAX = 0;


//======= definicja skladnika statycznego kluczeMapy ===========================
/**
 * Skladnik statyczny do przechowywania wzoru kluczy mapy konfiguracyjnej.
 */
vector<string> ObiektARMAX::kluczeMapy = inicjalizujKlucze();


//======= funkcja inicjalizujKlucze ============================================
/**
 * Skladnik statyczny do przechowywania wzoru kluczy mapy konfiguracyjnej.
 */
vector<string> ObiektARMAX :: inicjalizujKlucze(){
	vector<string> wzor;
	wzor.push_back("[nazwa]=");
	wzor.push_back("[dA]=");
	wzor.push_back("[dB]=");
	wzor.push_back("[dC]=");
	wzor.push_back("[paramA1]=");
	wzor.push_back("[paramA2]=");
	wzor.push_back("[paramB1]=");
	wzor.push_back("[paramB2]=");
	wzor.push_back("[paramC1]=");
	wzor.push_back("[paramC2]=");
	wzor.push_back("[yMin]=");
	wzor.push_back("[yMax]=");
	wzor.push_back("[var]=");
	wzor.push_back("[k]=");
	wzor.push_back("[nie1]=");
	wzor.push_back("[nie2]=");
	wzor.push_back("[typ]=");
	return wzor;
}

//======= konstruktor bezargumentowy ===========================================
/**
 * Konstruktor bezargumentowy - domniemany. Tworzy obiekt typu ARMAX o transmitancji
 * dyskretnej 1 i braku opoznienia.
 */
ObiektARMAX::ObiektARMAX() :
wielomianA(1.0),
wielomianB(1.0),
wielomianC(1.0),
wielomianA1(wielomianA),
wielomianB1(wielomianB),
wielomianC1(wielomianC),
wielomianA2(wielomianA),
wielomianB2(wielomianB),
wielomianC2(wielomianC) {
    czasDyskretny = 0;
    czasNie1 = 0;
    czasNie2 = 0;
    stopienA = 0;
    stopienB = 0;
    stopienC = 0;
    yMax = 100000;
    yMin = -100000;
    k = 0;
    warSzumu = 0;
    jestStabilny = true;
    jestNiestacjonarny = false;
    pamiecY.reserve(stopienA + 1);
    pamiecY.assign(stopienA + 1, 0.0);
    pamiecU.reserve(stopienB + k + 1);
    pamiecU.assign(stopienB + k + 1, 0.0);
    pamiecE.reserve(stopienC + 1);
    pamiecE.assign(stopienC + 1, 0.0);
    liczbaObiektowARMAX++;
    stringstream strumien;
    strumien << liczbaObiektowARMAX;
    nazwaObiektu = "Obiekt ARMAX nr" + strumien.str();
    //cout<<"tworzymy: "<<nazwaObiektu<<endl;
}

//============================================================================== 

/**  konstruktor kopiujący
 *
 *      @param &wzor - referencja do obiektu klasy ObiektARMAX, który ma być skopiowany.
 */
ObiektARMAX::ObiektARMAX(const ObiektARMAX &wzor) {
    //cout<<"kopiujacy";
    wielomianA = wzor.wielomianA1;
    wielomianB = wzor.wielomianB1;
    wielomianC = wzor.wielomianC1;
    wielomianA1 = wzor.wielomianA1;
    wielomianB1 = wzor.wielomianB1;
    wielomianC1 = wzor.wielomianC1;
    wielomianA2 = wzor.wielomianA2;
    wielomianB2 = wzor.wielomianB2;
    wielomianC2 = wzor.wielomianC2;
    stopienA = wzor.stopienA;
    stopienB = wzor.stopienB;
    stopienC = wzor.stopienC;
    czasDyskretny = wzor.czasDyskretny;
    czasNie1 = wzor.czasNie1;
    czasNie2 = wzor.czasNie2;
    przyrostA = wzor.przyrostA;
    przyrostB = wzor.przyrostB;
    przyrostC = wzor.przyrostC;
    yMax = wzor.yMax;
    yMin = wzor.yMin;
    jestStabilny = wzor.jestStabilny;
    jestNiestacjonarny = wzor.jestNiestacjonarny;
    k = wzor.k;
    warSzumu = wzor.warSzumu;
    wspPrzeskalowania = wzor.wspPrzeskalowania;
    pamiecY = wzor.pamiecY;
    pamiecU = wzor.pamiecU;
    pamiecE = wzor.pamiecE;
    liczbaObiektowARMAX++;
	nazwaObiektu = wzor.nazwaObiektu;
    //cout<<"tworzymy: "<<nazwaObiektu<<"  [kopia "<<wzor.nazwaObiektu<<"]"<<endl;
}

//============================================================================== 

/** Konstruktor tworzacy obiekt ARMAX na podstawie podanych wektorow wspolczynnikow
 * wielomianow A, B i C oraz opoznienia k. Stopnie wielomianow sa generowane 
 * automatycznie.
 *
 *      @param wA - wektor wspolczynnikow wielomianu A
 *      @param wB - wektor wspolczynnikow wielomianu B
 *      @param wC - wektor wspolczynnikow wielomianu C
 *      @param k - dyskretny czas opoznienia
 */
ObiektARMAX::ObiektARMAX(vector<double> wA,
        vector<double> wB,
        vector<double> wC,
        int k) :
wielomianA(wA),
wielomianB(wB),
wielomianC(wC),
wielomianA1(wA),
wielomianB1(wB),
wielomianC1(wC),
wielomianA2(wA),
wielomianB2(wB),
wielomianC2(wC) {
    this->k = k;
    this->warSzumu = 0;
    this->yMax = 1000000;
    this->yMin = -1000000;
    stopienA = wielomianA.pobierzStopien();
    stopienB = wielomianB.pobierzStopien();
    stopienC = wielomianC.pobierzStopien();
    wielomianA = wielomianA.normalizujWielomian(wielomianA[stopienA]);
    wielomianA1 = wielomianA1.normalizujWielomian(wielomianA1[stopienA]);
    wielomianA2 = wielomianA2.normalizujWielomian(wielomianA2[stopienA]);
    jestStabilny = this->sprawdzStabilnosc();
    jestNiestacjonarny = false;
    czasDyskretny = 0;
    czasNie1 = 0;
    czasNie2 = 0;
    pamiecY.reserve(stopienA + 1);
    pamiecY.assign(stopienA + 1, 0.0);
    pamiecU.reserve(stopienB + k + 1);
    pamiecU.assign(stopienB + k + 1, 0.0);
    pamiecE.reserve(stopienC + 1);
    pamiecE.assign(stopienC + 1, 0.0);
    liczbaObiektowARMAX++;
    stringstream strumien;
    strumien << liczbaObiektowARMAX;
    nazwaObiektu = "Obiekt ARMAX nr" + strumien.str();
    //cout<<"tworzymy: "<<nazwaObiektu<<endl;
}

//============================================================================== 

/** Konstruktor tworzacy obiekt ARMAX z nasyceniem na podstawie podanych wektorow 
 * wspolczynnikow wielomianow A, B i C oraz opoznienia k. Stopnie wielomianow 
 * sa generowane automatycznie.
 *
 *      @param wA - wektor wspolczynnikow wielomianu A
 *      @param wB - wektor wspolczynnikow wielomianu B
 *      @param wC - wektor wspolczynnikow wielomianu C
 *      @param k - dyskretny czas opoznienia
 *      @param yMax - wartosc maksymalna nasycenia wyjscia obiektu
 *      @param yMin - wartosc minimalna nasycenia wyjscia obiektu
 */
ObiektARMAX::ObiektARMAX(vector<double> wA,
        vector<double> wB,
        vector<double> wC,
        int k,
        double yMax,
        double yMin) :
wielomianA(wA),
wielomianB(wB),
wielomianC(wC),
wielomianA1(wA),
wielomianB1(wB),
wielomianC1(wC),
wielomianA2(wA),
wielomianB2(wB),
wielomianC2(wC) {
    this->k = k;
    this->warSzumu = 0;
    this->wspPrzeskalowania =
            GenLosowy::pobierzInstancje().obliczWspPrzeskalowaniaCiagu(warSzumu, wielomianA, wielomianC);
    stopienA = wielomianA.pobierzStopien();
    stopienB = wielomianB.pobierzStopien();
    stopienC = wielomianC.pobierzStopien();
    wielomianA = wielomianA.normalizujWielomian(wielomianA[stopienA]);
    wielomianA1 = wielomianA1.normalizujWielomian(wielomianA1[stopienA]);
    wielomianA2 = wielomianA2.normalizujWielomian(wielomianA2[stopienA]);
    jestStabilny = this->sprawdzStabilnosc();
    jestNiestacjonarny = false;
    this->yMax = yMax;
    this->yMin = yMin;
    czasDyskretny = 0;
    czasNie1 = 0;
    czasNie2 = 0;
    pamiecY.reserve(stopienA + 1);
    pamiecY.assign(stopienA + 1, 0.0);
    pamiecU.reserve(stopienB + k + 1);
    pamiecU.assign(stopienB + k + 1, 0.0);
    pamiecE.reserve(stopienC + 1);
    pamiecE.assign(stopienC + 1, 0.0);
    liczbaObiektowARMAX++;
    stringstream strumien;
    strumien << liczbaObiektowARMAX;
    nazwaObiektu = "Obiekt ARMAX nr" + strumien.str();
    //cout<<"tworzymy: "<<nazwaObiektu<<endl;
}

//============================================================================== 
/** Konstruktor tworzacy obiekt ARMAX z nasyceniem i niestacjonarnoscia 
 * na podstawie podanych wektorow wspolczynnikow wielomianow A, A2, B, B2, C i C2 
 * oraz opoznienia k. Stopnie wielomianow sa generowane automatycznie.
 *
 *      @param wA - wektor wspolczynnikow wielomianu A
 *      @param wB - wektor wspolczynnikow wielomianu B
 *      @param wC - wektor wspolczynnikow wielomianu C
 *      @param wA2 - wektor wspolczynnikow wielomianu A2
 *      @param wB2 - wektor wspolczynnikow wielomianu B2
 *      @param wC2 - wektor wspolczynnikow wielomianu C2
 *      @param k - dyskretny czas opoznienia
 *      @param czasNie1 - czas poczatku niestacjonarnosci
 *      @param czasNie2 - czas zakonczenia zmian w obiekcie
 *      @param yMax - wartosc maksymalna nasycenia wyjscia obiektu
 *      @param yMin - wartosc minimalna nasycenia wyjscia obiektu
 *      @param warSzumu - wariancja wyjścia toru zakłócenia
 */
ObiektARMAX::ObiektARMAX(vector<double> wA,
        vector<double> wB,
        vector<double> wC,
        vector<double> wA2,
        vector<double> wB2,
        vector<double> wC2,
        int k,
        int czasNie1,
        int czasNie2,
        double yMax,
        double yMin,
        double warSzumu) :
wielomianA(wA),
wielomianB(wB),
wielomianC(wC),
wielomianA1(wA),
wielomianB1(wB),
wielomianC1(wC),
wielomianA2(wA2),
wielomianB2(wB2),
wielomianC2(wC2) {

    
/*
    //-------- zapisanie stopni wielomianow -----------------------------------
    stopienA = wielomianA.pobierzStopien();
    if (stopienA != wielomianA2.pobierzStopien()) cout << "wielomiany A maja rozny stopien";
    stopienB = wielomianB.pobierzStopien();
    if (stopienB != wielomianB2.pobierzStopien()) cout << "wielomiany B maja rozny stopien";
    stopienC = wielomianC.pobierzStopien();
    if (stopienC != wielomianC2.pobierzStopien()) cout << "wielomiany C maja rozny stopien";

    //-------- normalizacja wielomianu A --------------------------------------
    wielomianA = wielomianA.normalizujWielomian(wielomianA[stopienA]);
    wielomianA1 = wielomianA1.normalizujWielomian(wielomianA1[stopienA]);
    wielomianA2 = wielomianA2.normalizujWielomian(wielomianA2[wielomianA2.pobierzStopien()]);

    //cout<<"wielomian A1 ="<<wielomianA1<<endl;
    //cout<<"wielomian A2 ="<<wielomianA2<<endl;

    //-------- niestacjonarnosc -----------------------------------------------
    if ((wielomianA1 == wielomianA2) && (wielomianB1 == wielomianB2) && (wielomianC1 == wielomianC2)) {
        //wszystkie wielomiany są jednakowe
        jestNiestacjonarny = false; //wiec obiekt jest stacjonarny
    }else { //są rozne wiec jest niestacjonarny
        jestNiestacjonarny = true;
        //cout<<"PRZYROSTY!!!"<<endl;
        //----------- rezerwacja pamieci --------------------------------------
        przyrostA.reserve(wielomianA2.pobierzStopien() + 1);
        przyrostB.reserve(wielomianB2.pobierzStopien() + 1);
        przyrostC.reserve(wielomianC2.pobierzStopien() + 1);
        przyrostA.assign(wielomianA2.pobierzStopien() + 1, 0.0);
        przyrostB.assign(wielomianB2.pobierzStopien() + 1, 0.0);
        przyrostC.assign(wielomianC2.pobierzStopien() + 1, 0.0);
        //----------- wielomian A ---------------------------------------------
        for (int i = 0; i <= wielomianA2.pobierzStopien(); i++) {
            //cout<<wielomianA2[i]<<wielomianA1[i]<<i<<endl;
            przyrostA.at(i) = (wielomianA2[i] - wielomianA1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostA"<<i<<"  "<<przyrostA.at(i);
        }
        //----------- wielomian B ---------------------------------------------
        for (int i = 0; i <= wielomianB2.pobierzStopien(); i++) {
            przyrostB.at(i) = (wielomianB2[i] - wielomianB1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostB"<<i<<"  "<<przyrostB.at(i);
        }
        //----------- wielomian C ---------------------------------------------
        for (int i = 0; i <= wielomianC2.pobierzStopien(); i++) {
            przyrostC.at(i) = (wielomianB2[i] - wielomianB1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostC"<<i<<"  "<<przyrostC.at(i);
        }
    }

    //-------- sprawdzenie stabilnosci ----------------------------------------
    jestStabilny = this->sprawdzStabilnosc();
	pamiecY.reserve(stopienA + 1);
    pamiecY.assign(stopienA + 1, 0.0);
    pamiecU.reserve(stopienB + k + 1);
    pamiecU.assign(stopienB + k + 1, 0.0);
    pamiecE.reserve(stopienC + 1);
    pamiecE.assign(stopienC + 1, 0.0);
*/	
	this->k = k;
    this->yMax = yMax;
    this->yMin = yMin;
    //czasDyskretny = 0;
    this->czasNie1 = czasNie1;
    this->czasNie2 = czasNie2;
    this->warSzumu = warSzumu;
    //this->wspPrzeskalowania =
            //GenLosowy::pobierzInstancje().obliczWspPrzeskalowaniaCiagu(warSzumu, wielomianA, wielomianC);
    inicjalizuj();
    liczbaObiektowARMAX++;
    stringstream strumien;
    strumien << liczbaObiektowARMAX;
    nazwaObiektu = "Obiekt ARMAX nr" + strumien.str();
    //cout<<"tworzymy: "<<nazwaObiektu<<endl;
}

//============================================================================== 
/** Konstruktor tworzacy obiekt ARMAX z nasyceniem i niestacjonarnoscia 
 * na podstawie podanych wektorow wspolczynnikow wielomianow A, A2, B, B2, C i C2 
 * oraz opoznienia k. Stopnie wielomianow sa generowane automatycznie.
 *
 *      @param wA - wektor wspolczynnikow wielomianu A
 *      @param wB - wektor wspolczynnikow wielomianu B
 *      @param wC - wektor wspolczynnikow wielomianu C
 *      @param wA2 - wektor wspolczynnikow wielomianu A2
 *      @param wB2 - wektor wspolczynnikow wielomianu B2
 *      @param wC2 - wektor wspolczynnikow wielomianu C2
 *      @param k - dyskretny czas opoznienia
 *      @param czasNie1 - czas poczatku niestacjonarnosci
 *      @param czasNie2 - czas zakonczenia zmian w obiekcie
 *      @param yMax - wartosc maksymalna nasycenia wyjscia obiektu
 *      @param yMin - wartosc minimalna nasycenia wyjscia obiektu
 *      @param warSzumu - wariancja wyjścia toru zakłócenia
 *      @param nazwaObiektu - nazwa obiektu
 */
ObiektARMAX::ObiektARMAX(vector<double> wA,
        vector<double> wB,
        vector<double> wC,
        vector<double> wA2,
        vector<double> wB2,
        vector<double> wC2,
        int k,
        int czasNie1,
        int czasNie2,
        double yMax,
        double yMin,
        double warSzumu,
        string nazwaObiektu) :
wielomianA(wA),
wielomianB(wB),
wielomianC(wC),
wielomianA1(wA),
wielomianB1(wB),
wielomianC1(wC),
wielomianA2(wA2),
wielomianB2(wB2),
wielomianC2(wC2) {
    
    
/*
    //-------- zapisanie stopni wielomianow -----------------------------------
    stopienA = wielomianA.pobierzStopien();
    if (stopienA != wielomianA2.pobierzStopien()) cout << "wielomiany A maja rozny stopien";
    stopienB = wielomianB.pobierzStopien();
    if (stopienB != wielomianB2.pobierzStopien()) cout << "wielomiany B maja rozny stopien";
    stopienC = wielomianC.pobierzStopien();
    if (stopienC != wielomianC2.pobierzStopien()) cout << "wielomiany C maja rozny stopien";

    //-------- normalizacja wielomianu A --------------------------------------
    wielomianA = wielomianA.normalizujWielomian(wielomianA[stopienA]);
    wielomianA1 = wielomianA1.normalizujWielomian(wielomianA1[stopienA]);
    wielomianA2 = wielomianA2.normalizujWielomian(wielomianA2[wielomianA2.pobierzStopien()]);

    //cout<<"wielomian A1 ="<<wielomianA1<<endl;
    //cout<<"wielomian A2 ="<<wielomianA2<<endl;

    //-------- niestacjonarnosc -----------------------------------------------
    if ((wielomianA1 == wielomianA2) && (wielomianB1 == wielomianB2) && (wielomianC1 == wielomianC2)) {
        //wszystkie wielomiany są jednakowe
        jestNiestacjonarny = false; //wiec obiekt jest stacjonarny
    }else { //są rozne wiec jest niestacjonarny
        jestNiestacjonarny = true;
        //cout<<"PRZYROSTY!!!"<<endl;
        //----------- rezerwacja pamieci --------------------------------------
        przyrostA.reserve(wielomianA2.pobierzStopien() + 1);
        przyrostB.reserve(wielomianB2.pobierzStopien() + 1);
        przyrostC.reserve(wielomianC2.pobierzStopien() + 1);
        przyrostA.assign(wielomianA2.pobierzStopien() + 1, 0.0);
        przyrostB.assign(wielomianB2.pobierzStopien() + 1, 0.0);
        przyrostC.assign(wielomianC2.pobierzStopien() + 1, 0.0);
        //----------- wielomian A ---------------------------------------------
        for (int i = 0; i <= wielomianA2.pobierzStopien(); i++) {
            //cout<<wielomianA2[i]<<wielomianA1[i]<<i<<endl;
            przyrostA.at(i) = (wielomianA2[i] - wielomianA1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostA"<<i<<"  "<<przyrostA.at(i);
        }
        //----------- wielomian B ---------------------------------------------
        for (int i = 0; i <= wielomianB2.pobierzStopien(); i++) {
            przyrostB.at(i) = (wielomianB2[i] - wielomianB1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostB"<<i<<"  "<<przyrostB.at(i);
        }
        //----------- wielomian C ---------------------------------------------
        for (int i = 0; i <= wielomianC2.pobierzStopien(); i++) {
            przyrostC.at(i) = (wielomianB2[i] - wielomianB1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostC"<<i<<"  "<<przyrostC.at(i);
        }
    }

    //-------- sprawdzenie stabilnosci ----------------------------------------
    jestStabilny = this->sprawdzStabilnosc();
	pamiecY.reserve(stopienA + 1);
    pamiecY.assign(stopienA + 1, 0.0);
    pamiecU.reserve(stopienB + k + 1);
    pamiecU.assign(stopienB + k + 1, 0.0);
    pamiecE.reserve(stopienC + 1);
    pamiecE.assign(stopienC + 1, 0.0);
*/
    this->k = k;
	this->yMax = yMax;
    this->yMin = yMin;
    //czasDyskretny = 0;
    this->czasNie1 = czasNie1;
    this->czasNie2 = czasNie2;
    this->warSzumu = warSzumu;
    //this->wspPrzeskalowania =
            //GenLosowy::pobierzInstancje().obliczWspPrzeskalowaniaCiagu(warSzumu, wielomianA, wielomianC);
    liczbaObiektowARMAX++;
	inicjalizuj();
    this->nazwaObiektu = nazwaObiektu;
    //cout<<"tworzymy: "<<nazwaObiektu<<endl;
}

void ObiektARMAX::inicjalizuj(){
    this->czasDyskretny = 0;
	wielomianA = wielomianA1;
    wielomianB = wielomianB1;
    wielomianC = wielomianC1;
    //-------- zapisanie stopni wielomianow -----------------------------------
    stopienA = wielomianA.pobierzStopien();
    if (stopienA != wielomianA2.pobierzStopien()) cout << "wielomiany A maja rozny stopien";
    stopienB = wielomianB.pobierzStopien();
    if (stopienB != wielomianB2.pobierzStopien()) cout << "wielomiany B maja rozny stopien";
    stopienC = wielomianC.pobierzStopien();
    if (stopienC != wielomianC2.pobierzStopien()) cout << "wielomiany C maja rozny stopien";
    
    //-------- normalizacja wielomianu A --------------------------------------
    wielomianA = wielomianA.normalizujWielomian(wielomianA[stopienA]);
    wielomianA1 = wielomianA1.normalizujWielomian(wielomianA1[stopienA]);
    wielomianA2 = wielomianA2.normalizujWielomian(wielomianA2[wielomianA2.pobierzStopien()]);

    //cout<<"wielomian A1 ="<<wielomianA1<<endl;
    //cout<<"wielomian A2 ="<<wielomianA2<<endl;

    //-------- niestacjonarnosc -----------------------------------------------
    if ((wielomianA1 == wielomianA2) && (wielomianB1 == wielomianB2) && (wielomianC1 == wielomianC2)) {
        //wszystkie wielomiany są jednakowe
        jestNiestacjonarny = false; //wiec obiekt jest stacjonarny
    }else { //są rozne wiec jest niestacjonarny
        jestNiestacjonarny = true;
        //cout<<"PRZYROSTY!!!"<<endl;
        //----------- rezerwacja pamieci --------------------------------------
        przyrostA.reserve(wielomianA2.pobierzStopien() + 1);
        przyrostB.reserve(wielomianB2.pobierzStopien() + 1);
        przyrostC.reserve(wielomianC2.pobierzStopien() + 1);
        przyrostA.assign(wielomianA2.pobierzStopien() + 1, 0.0);
        przyrostB.assign(wielomianB2.pobierzStopien() + 1, 0.0);
        przyrostC.assign(wielomianC2.pobierzStopien() + 1, 0.0);
        //----------- wielomian A ---------------------------------------------
        for (int i = 0; i <= wielomianA2.pobierzStopien(); i++) {
            //cout<<wielomianA2[i]<<wielomianA1[i]<<i<<endl;
            przyrostA.at(i) = (wielomianA2[i] - wielomianA1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostA"<<i<<"  "<<przyrostA.at(i);
        }
        //----------- wielomian B ---------------------------------------------
        for (int i = 0; i <= wielomianB2.pobierzStopien(); i++) {
            przyrostB.at(i) = (wielomianB2[i] - wielomianB1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostB"<<i<<"  "<<przyrostB.at(i);
        }
        //----------- wielomian C ---------------------------------------------
        for (int i = 0; i <= wielomianC2.pobierzStopien(); i++) {
            przyrostC.at(i) = (wielomianC2[i] - wielomianC1[i]) / (czasNie2 - czasNie1);
            //cout<<"przyrostC"<<i<<"  "<<przyrostC.at(i);
        }
    }

    //-------- sprawdzenie stabilnosci ----------------------------------------
    this->jestStabilny = this->sprawdzStabilnosc();
    this->wspPrzeskalowania =
            GenLosowy::pobierzInstancje().obliczWspPrzeskalowaniaCiagu(warSzumu, wielomianA, wielomianC);
    pamiecY.reserve(stopienA + 1);
    pamiecY.assign(stopienA + 1, 0.0);
    pamiecU.reserve(stopienB + k + 1);
    pamiecU.assign(stopienB + k + 1, 0.0);
    pamiecE.reserve(stopienC + 1);
    pamiecE.assign(stopienC + 1, 0.0);
}


//======= destruktor  ==========================================================
/**
 * Destruktor klasy ObiektARMAX - aktualizuje liczbe obiektow ARMAX poprzez
 * odjecie 1 od liczbyObiektowARMAX.
 */
ObiektARMAX::~ObiektARMAX() {
    //cout<<"niszczymy: "<<nazwaObiektu<<endl;
    liczbaObiektowARMAX--;
}


//======= przeladowany operator przypisania ====================================
/** 
 * Przeladowany operator przypisania =. 
 *
 *     @param wzor - wielomian który ma być przypisany
 *     @return referencja do wielomianu wynikowego (do którego przypisano wzor)
 */
ObiektARMAX& ObiektARMAX::operator=(const ObiektARMAX &wzor) {
    wielomianA = wzor.wielomianA1;
    wielomianB = wzor.wielomianB1;
    wielomianC = wzor.wielomianC1;
    wielomianA1 = wzor.wielomianA1;
    wielomianB1 = wzor.wielomianB1;
    wielomianC1 = wzor.wielomianC1;
    wielomianA2 = wzor.wielomianA2;
    wielomianB2 = wzor.wielomianB2;
    wielomianC2 = wzor.wielomianC2;
    stopienA = wzor.stopienA;
    stopienB = wzor.stopienB;
    stopienC = wzor.stopienC;
    czasDyskretny = wzor.czasDyskretny;
    czasNie1 = wzor.czasNie1;
    czasNie2 = wzor.czasNie2;
    przyrostA = wzor.przyrostA;
    przyrostB = wzor.przyrostB;
    przyrostC = wzor.przyrostC;
    yMax = wzor.yMax;
    yMin = wzor.yMin;
    jestStabilny = wzor.jestStabilny;
    jestNiestacjonarny = wzor.jestNiestacjonarny;
    k = wzor.k;
    warSzumu = wzor.warSzumu;
    wspPrzeskalowania = wzor.wspPrzeskalowania;
    pamiecY = wzor.pamiecY;
    pamiecU = wzor.pamiecU;
    pamiecE = wzor.pamiecE;
    liczbaObiektowARMAX++;
    nazwaObiektu = wzor.nazwaObiektu;
    return *this;
}

//======= funkcja symuluj online ===============================================
/**
 * Funkcja symulujaca obiekt dyskretny - obliczajaca wyjscie z obiektu na 
 * podstawie obecnego stanu wejscia u. 
 *      @param u - skalarne wejscie obiektu
 *      @return wyjscie z obiektu
 */
double ObiektARMAX::symuluj(double u) {
    //-------- niestacjonarnosc ------------------------------------------------
    if (jestNiestacjonarny) { //obiekt jest niestacjonarny
        //czas w zakresie zmian parametrow
        if ((czasDyskretny >= czasNie1) && (czasDyskretny < czasNie2)) {
            for (int i = 0; i <= stopienA; i++) {
                wielomianA[i] += przyrostA.at(i);
            }
            for (int i = 0; i <= stopienB; i++) {
                wielomianB[i] = wielomianB[i] + przyrostB.at(i);
            }
            for (int i = 0; i <= stopienC; i++) {
                wielomianC[i] = wielomianC[i] + przyrostC.at(i);
            }
        }
    }
    //cout<<"\n\tu="<<u<<endl;
    double y = 0.0;

    pamiecU.at(stopienB + k) = u;
    //-------- wplyw wyjsc -----------------------------------------------------
	double tempInnerProd1 = inner_product(pamiecY.rbegin() + 1, pamiecY.rend(), wielomianA.pobierzIteratorPoczatek() + 1, 0.0);
    y -= tempInnerProd1;
    for (int i = 0; i < stopienA; i++) {
        //y += -wielomianA[i]*pamiecY.at(i);
        if (i != 0) pamiecY.at(i - 1) = pamiecY.at(i);
    }
    //-------- wplyw wejsc -----------------------------------------------------
    double tempInnerProd2 = inner_product(pamiecU.rbegin() + k, pamiecU.rend(), wielomianB.pobierzIteratorPoczatek(), 0.0);
	y += tempInnerProd2;
    for (int i = 0; i <= stopienB + k; i++) {
        //if( i<=stopienB ) y += wielomianB[i]*pamiecU.at(i);
        if (i != 0) pamiecU.at(i - 1) = pamiecU.at(i);
    }
    //-------- wplyw zaklocen --------------------------------------------------
    if (warSzumu != 0.0) {
        pamiecE.at(stopienC) = //pobranie losowego wejscia toru zaklocen
                //przeskalowanego by otrzymac zadana wariancje na wyjsciu
                wspPrzeskalowania * GenLosowy::pobierzInstancje().pobierzLiczbeLosowa();

        //cout << "losowa = " << pamiecE.at(stopienC) << endl;
        y += inner_product(pamiecE.rbegin(), pamiecE.rend(), wielomianC.pobierzIteratorPoczatek(), 0.0);
        for (int i = 0; i <= stopienC; i++) {
            //y += wielomianC[i]*pamiecE.at(i);
            if (i != 0) pamiecE.at(i - 1) = pamiecE.at(i);
        }
    }

    //-------- nasycenie -------------------------------------------------------
    if (y > yMax) {
        y = yMax;
    }else if (y < yMin) {
        y = yMin;
    }
    //cout<<"\ty"<<czasDyskretny<<" = "<<y<<endl<<endl;
    pamiecY.at(stopienA - 1) = y; //zapisanie obecnego wyjscia do pamieci
    czasDyskretny++; //nastepna chwila czasu
    return y;
}

//======= funkcja zmierzWyjscie ================================================
/**
 * Funkcja probkujaca ObiektARMAX. Funkcja symuluje dzialanie czujnika pomiarowego
 * - pobiera wartosc wyjscia obiektu w kolejnej chwili czasu - przyszlej probce 
 * przy zalozeniu braku zmiany sterowania.
 *      @return przyszle wyjscie z obiektu
 */
double ObiektARMAX :: zmierzWyjscie(){
	double zmierzoneWyjscie;
	vector<double> tymczPamY = pamiecY, tymczPamU = pamiecU, tymczPamE = pamiecE;
	int tymczCzas = czasDyskretny;
	Wielomian tymczWielA = wielomianA, tymczWielB = wielomianB, tymczWielC = wielomianC;
	zmierzoneWyjscie = symuluj(pamiecU.back());
	pamiecY = tymczPamY;
	pamiecU = tymczPamU;
	pamiecE = tymczPamE;
	wielomianA = tymczWielA;
	wielomianB = tymczWielB;
	wielomianC = tymczWielC;
	czasDyskretny = tymczCzas;
	return zmierzoneWyjscie;
}

//======= funkcja symuluj offline ==============================================
/**
 * Funkcja symulujaca obiekt dyskretny - obliczajaca wektor wyjscia z obiektu 
 * na podstawie wektora wejść. 
 *      @param u - wektor wejscia obiektu
 *      @return wektor wyjscie z obiektu 
 */
vector<double> ObiektARMAX::symuluj(const vector<double> & u) {
    vector<double> y(u.size(), 0.0); //utworzenie zerowego wektora wyjsc
    for (int i = 0; i < u.size(); i++) { //petla czasu dyskretnego
        y.at(i) = symuluj(u.at(i)); //wywolanie symulacji krokowej
    }
    return y;
}


//======= funkcja sprawdzStabilnosc ============================================
/**
 * Funkcja sprawdzajaca stabilnosc. Sprawdzenie stabilnosci opiera sie na wyznaczeniu
 * pierwiastkow wielomianu A metoda Laguerra. 
 *      @return true jesli obiekt jest stabilny
 */
bool ObiektARMAX::sprawdzStabilnosc() const {
    //-------- obliczenie pierwiastkow metoda Laguerra -------------------------
    vector<complex<double> > pierwiastki = wielomianA.pierwiastkiL(0.00001, 1.1);
    //-------- sprawdzenie czy pierwiastki znajduja sie w kole jednostkowym ----
    for (int i = 0; i < pierwiastki.size(); i++) {
        if (abs(pierwiastki.at(i)) > 1) {
            return false;
        }
    }
    return true;
}


//======= funkcja zerujPamiec ==================================================
/**
 * Funkcja zerujaca wartosci pamieci obiektu i czasu dyskretnego. Uruchomienie 
 * metody przygotowuje do rozpoczecia nowej symulacji.
 */
void ObiektARMAX::zerujPamiec() {
    pamiecY.assign(stopienA + 1, 0.0);
    pamiecU.assign(stopienB + k + 1, 0.0);
    pamiecE.assign(stopienC + 1, 0.0);
    wielomianA = wielomianA1;
    wielomianB = wielomianB1;
    wielomianC = wielomianC1;
    czasDyskretny = 0;
}


//======= funkcja wypiszInfo ===================================================
/**
 * Funkcja wypisujaca informacje o obiekcie dyskretnym typu ARMAX. Wypisuje 
 * nazwe obiektu.
 */
void ObiektARMAX::wypiszInfo() const {
    cout << endl << "\t==== INFO ==== " << endl;
    cout << "\tnazwa: " << nazwaObiektu << endl;
    if (jestStabilny == true) {
        cout << "\tstabilny" << endl;
    }else {
        cout << "\tniestabilny" << endl;
    }
    if (jestNiestacjonarny == true) {
        cout << "\tniestacjonarny" << endl;
        //wypiszVector(przyrostA);
        //wypiszVector(przyrostB);
        //wypiszVector(przyrostC);
    }else {
        cout << "\tstacjonarny" << endl;
    }
    cout << "\twielomianA1 [dA=" << stopienA << "]: " << wielomianA1 << endl;
    cout << "\twielomianB1 [dB=" << stopienB << "]: " << wielomianB1 << endl;
    cout << "\twielomianC1 [dC=" << stopienC << "]: " << wielomianC1 << endl;
    cout << "\twielomianA2 [dA=" << stopienA << "]: " << wielomianA2 << endl;
    cout << "\twielomianB2 [dB=" << stopienB << "]: " << wielomianB2 << endl;
    cout << "\twielomianC2 [dC=" << stopienC << "]: " << wielomianC2 << endl;
    cout << "\topoznienie: " << k << endl;
    cout << "\tnasycenie maks: " << yMax << endl;
    cout << "\tnasycenie min: " << yMin << endl;
    cout << "\twariancja szumu: " << warSzumu << endl;
}

//======= funkcja wypiszObecneParametry ========================================
/**
 * Funkcja wypisujaca bieżące parametry obiektu. Metoda umożliwia wypisanie 
 * parametrow obiektu w biezacej chwili czasu.
 */
void ObiektARMAX::wypiszObecneParametry() const {
    cout << endl << "\t==== OBECNE PARAMETRY ==== " << endl;
    cout << "\tnazwa: " << nazwaObiektu << endl;
    cout << "\tczas: " << czasDyskretny << endl;
    cout << "\twielomianA [dA=" << stopienA << "]: " << wielomianA << endl;
    cout << "\twielomianB [dB=" << stopienB << "]: " << wielomianB << endl;
    cout << "\twielomianC [dC=" << stopienC << "]: " << wielomianC << endl;
}

//======= funkcja pobierzNazwe =================================================
/**
 * Funkcja zwracajaca wartosc nazwy obiektu klasy ObiektARMAX.
 *      
 *      @return nazwa obiektu
 */
string ObiektARMAX::pobierzNazwe() const {
    return nazwaObiektu;
}

//======= funkcja ustawParametry  =============================================
/**
 * Ustawia parametry obiektu na podstawie otrzymanej mapy. Metoda wykorzystywana
 * podczas wczytywania obiektow z pliku konfiguracyjnego. Zwracana wartosc zalezy
 * od poprawnosci mapy podanej jako argument
 *		@param mapaKonfig - mapa z konfiguracja
 *		@return <code>true</code> jezeli mapa byla poprawna mapa ObiektuARMAX i 
 *				i udalo sie ustawic parametry
 *	            <code>false</code> w przeciwnym wypadku
 */
bool ObiektARMAX::ustawParametry(std::map<std::string, std::string> mapaKonfig) {
    double  tymczYMax, tymczYMin, tymczVar;
	int tymczStA, tymczStB, tymczStC, tymczK, tymczNie1, tymczNie2;
	vector<double> tymczWspA1, tymczWspA2, tymczWspB1, tymczWspB2, tymczWspC1, tymczWspC2;
	string tymczNazwa;
	double tymcz = 0;
    stringstream strumien;
	//----- ustawienie wyjatkow w strumieniu -----------------------------------
    strumien.exceptions(stringstream::badbit | stringstream::failbit);
	
	//----- sprawdzenie wielkosci mapy -----------------------------------------
	if ( mapaKonfig.size() != ObiektARMAX::kluczeMapy.size())
		throw Wyjatek ("Blad w funkcji ObiektARMAX::ustawParametry w obiekcie: "+this->pobierzNazwe()
				+"./n Nie zmieniono parametrow!");

	//----- sprawdzenie poprawnosci kluczy mapy --------------------------------
	for ( int i = 0; i < ObiektARMAX::kluczeMapy.size(); i++){
		if ( mapaKonfig[ObiektARMAX::kluczeMapy.at(i)].empty() )
			throw Wyjatek ("Blad w funkcji ObiektARMAX::ustawParametry w obiekcie: "+this->pobierzNazwe()
				+"./n Nie zmieniono parametrow!"
				+"/n Brak klucza:"+ ObiektARMAX::kluczeMapy.at(i));
	}
	
	//----- petla po elementach mapy -------------------------------------------
    for (map<string, string>::iterator it = mapaKonfig.begin(); it != mapaKonfig.end(); it++) {
        strumien.clear();
        strumien.str(it->second);   //wpisanie do strumienia wartosci mapy
        try {
            if ((it->first) == ("[nazwa]=")) strumien >> tymczNazwa;
            else if ((it->first) == ("[k]=")) strumien >> tymczK;
            else if ((it->first) == ("[dA]=")) strumien >> tymczStA;
            else if ((it->first) == ("[paramA1]=")){ 
                for (int i = 0; i <= tymczStA; i++) {strumien>>tymcz; tymczWspA1.push_back(tymcz);}
            }
            else if ((it->first) == ("[paramA2]=")){ 
                for (int i = 0; i <= tymczStA; i++) {strumien>>tymcz; tymczWspA2.push_back(tymcz);}
            }
            else if ((it->first) == ("[dB]=")) strumien >> tymczStB;
            else if ((it->first) == ("[paramB1]=")) { 
                for (int i = 0; i <= tymczStB; i++) {strumien>>tymcz; tymczWspB1.push_back(tymcz);}
            }
            else if ((it->first) == ("[paramB2]=")) { 
                for (int i = 0; i <= tymczStB; i++) {strumien>>tymcz; tymczWspB2.push_back(tymcz);}
            }
            else if ((it->first) == ("[dC]=")) strumien >> tymczStC;
            else if ((it->first) == ("[paramC1]=")) { 
                for (int i = 0; i <= tymczStC; i++) {strumien>>tymcz; tymczWspC1.push_back(tymcz);}
            }
            else if ((it->first) == ("[paramC2]=")) { 
                for (int i = 0; i <= tymczStC; i++) {strumien>>tymcz; tymczWspC2.push_back(tymcz);}
            }
            else if ((it->first) == ("[nie1]=")) strumien >> tymczNie1;
            else if ((it->first) == ("[nie2]=")) strumien >> tymczNie2;
            else if ((it->first) == ("[yMax]=")) strumien >> tymczYMax;
            else if ((it->first) == ("[yMin]=")) strumien >> tymczYMin;
            else if ((it->first) == ("[var]=")) strumien >> tymczVar;
        }catch (stringstream::failure e) {  //wyjatek strumienia - nie wczytano poprawnie parametrow
            strumien.clear();
			throw Wyjatek("Blad przy ustawianiu parametrow obiektu: "+this->pobierzNazwe()
				+"./n Nie zmieniono parametrow!");
            return false;
        }
    }
	// ---- przepisanie wartosci tymczasowych do parametrow obiektu -----------
	this->nazwaObiektu = tymczNazwa; this->k = tymczK;
	this->stopienA = tymczStA; this->wielomianA1 = Wielomian(tymczWspA1);
	this->wielomianA2 = Wielomian(tymczWspA2);
	this->stopienB = tymczStB; this->wielomianB1 = Wielomian(tymczWspB1);
	this->wielomianB2 = Wielomian(tymczWspB2);
	this->stopienC = tymczStC; this->wielomianC1 = Wielomian(tymczWspC1);
	this->wielomianC2 = Wielomian(tymczWspC2);
	this->czasNie1 = tymczNie1; this->czasNie2 = tymczNie2;
	this->yMax = tymczYMax; this->yMin = tymczYMin; this->warSzumu = tymczVar;

    this->inicjalizuj();
    this->zerujPamiec();
    return true;
}

//======= funkcja ustawParametry  =============================================
/**
 * Ustawia parametry obiektu na podstawie otrzymanej mapy. Metoda wykorzystywana
 * podczas wczytywania obiektow z pliku konfiguracyjnego. Zwracana wartosc zalezy
 * od poprawnosci mapy podanej jako argument
 *		@param mapaKonfig - mapa z konfiguracja
 *		@return <code>true</code> jezeli mapa byla poprawna mapa ObiektuARMAX i 
 *				i udalo sie ustawic parametry
 *	            <code>false</code> w przeciwnym wypadku
 */
/*
bool ObiektARMAX::ustawParametry(std::map<std::string, std::string> mapaKonfig) {
    double tymcz = 0;
    stringstream strumien;
    strumien.exceptions(stringstream::badbit | stringstream::failbit);
    vector<double> wsp;

    for (map<string, string>::iterator it = mapaKonfig.begin(); it != mapaKonfig.end(); it++) {
        strumien.clear();
        strumien.str(it->second);   //wpisanie do strumienia wartosci mapy
        try {
            if ((it->first) == ("[nazwa]=")) strumien >> this->nazwaObiektu;
            else if ((it->first) == ("[k]=")) strumien >> this->k;
            else if ((it->first) == ("[dA]=")) strumien >> this->stopienA;
            else if ((it->first) == ("[paramA1]=")){ 
                for (int i = 0; i <= stopienA; i++) {strumien>>tymcz; wsp.push_back(tymcz);}
                wielomianA = Wielomian(wsp);
                wielomianA1 = Wielomian(wsp); wsp.clear();
            }
            else if ((it->first) == ("[paramA2]=")){ 
                for (int i = 0; i <= stopienA; i++) {strumien>>tymcz; wsp.push_back(tymcz);}
                wielomianA2 = Wielomian(wsp); wsp.clear();
            }
            else if ((it->first) == ("[dB]=")) strumien >> this->stopienB;
            else if ((it->first) == ("[paramB1]=")) { 
                for (int i = 0; i <= stopienB; i++) {strumien>>tymcz; wsp.push_back(tymcz);}
                wielomianB = Wielomian(wsp);
                wielomianB1 = Wielomian(wsp); wsp.clear();
            }
            else if ((it->first) == ("[paramB2]=")) { 
                for (int i = 0; i <= stopienB; i++) {strumien>>tymcz; wsp.push_back(tymcz);}
                wielomianB2 = Wielomian(wsp); wsp.clear();
            }
            else if ((it->first) == ("[dC]=")) strumien >> this->stopienC;
            else if ((it->first) == ("[paramC1]=")) { 
                for (int i = 0; i <= stopienC; i++) {strumien>>tymcz; wsp.push_back(tymcz);}
                wielomianC = Wielomian(wsp);
                wielomianC1 = Wielomian(wsp); wsp.clear();
            }
            else if ((it->first) == ("[paramC2]=")) { 
                for (int i = 0; i <= stopienC; i++) {strumien>>tymcz; wsp.push_back(tymcz);}
                wielomianC2 = Wielomian(wsp); wsp.clear();
            }
            else if ((it->first) == ("[nie1]=")) strumien >> this->czasNie1;
            else if ((it->first) == ("[nie2]=")) strumien >> this->czasNie2;
            else if ((it->first) == ("[yMax]=")) strumien >> this->yMax;
            else if ((it->first) == ("[yMin]=")) strumien >> this->yMin;
            else if ((it->first) == ("[var]=")) strumien >> this->warSzumu;
        }catch (stringstream::failure e) {  //wyjatek strumienia - nie wczytano poprawnie parametrow
            strumien.clear();
            //cout << "wyjatek w mapie!" << endl;
            return false;
        }
    }
    //cout<<endl<<"wczytanoooo"<<endl;
    this->inicjalizuj();
    this->wypiszInfo();
    this->zerujPamiec();
    return true;
}*/


//======= funkcja pobierzParametry  =============================================
/**
 * Pobiera parametry ObiektuARMAX. Parametry sa zwracane w mapie zgodnej z zapisem
 * pliku konfiguracyjnego
 *		@return mapa z konfiguracja
 */
map<string, string> ObiektARMAX::pobierzParametry () const{
	map<string, string> mapaKonfig;
	stringstream strumien;
	mapaKonfig["[typ]="] = "armax";
	mapaKonfig["[nazwa]="] = this->nazwaObiektu;
	strumien << this->k; mapaKonfig["[k]="] = strumien.str(); strumien.str("");
	strumien << this->stopienA; mapaKonfig["[dA]="] = strumien.str();  strumien.str("");
	for (int i = 0; i <= stopienA; i++) strumien<< wielomianA1.pobierzWsp(stopienA-i) << " "; 
	mapaKonfig["[paramA1]="] = strumien.str();  strumien.str("");
	for (int i = 0; i <= stopienA; i++) strumien<< wielomianA2.pobierzWsp(stopienA-i) << " "; 
	mapaKonfig["[paramA2]="] = strumien.str();  strumien.str("");
	
	strumien << this->stopienB; mapaKonfig["[dB]="] = strumien.str();  strumien.str("");
	for (int i = 0; i <= stopienB; i++) strumien<< wielomianB1.pobierzWsp(stopienB-i) << " "; 
	mapaKonfig["[paramB1]="] = strumien.str();  strumien.str("");
	for (int i = 0; i <= stopienB; i++) strumien<< wielomianB2.pobierzWsp(stopienB-i) << " "; 
	mapaKonfig["[paramB2]="] = strumien.str();  strumien.str("");
	
	strumien << this->stopienC; mapaKonfig["[dC]="] = strumien.str();  strumien.str("");
	for (int i = 0; i <= stopienC; i++) strumien<< wielomianC1.pobierzWsp(stopienC-i) << " "; 
	mapaKonfig["[paramC1]="] = strumien.str();  strumien.str("");
	for (int i = 0; i <= stopienC; i++) strumien<< wielomianC2.pobierzWsp(stopienC-i) << " "; 
	mapaKonfig["[paramC2]="] = strumien.str();  strumien.str("");
	
	strumien << this->czasNie1; mapaKonfig["[nie1]="] = strumien.str();  strumien.str("");
	strumien << this->czasNie2; mapaKonfig["[nie2]="] = strumien.str();  strumien.str("");

	strumien << this->yMax; mapaKonfig["[yMax]="] = strumien.str();  strumien.str("");
	strumien << this->yMin; mapaKonfig["[yMin]="] = strumien.str();  strumien.str("");

	strumien << this->warSzumu; mapaKonfig["[var]="] = strumien.str();  strumien.str("");

	return mapaKonfig;
}
//------------------------------------------------------------------------------
//-------------- KONIEC [klasa ObiektARMAX] ------------------------------------
//------------------------------------------------------------------------------



